#include "stdio.h"
#include "string.h"
#include "stdbool.h"
#include "stdlib.h"
#include "cim_function.h"
#define  TEST_NUM 4  
#define  CIM_ROW  14 
#define  CIM_COL  4  
#define  SHIFT_NUM  15

void read_bin_file(const char * filename, signed char * data, size_t size){
    FILE *file = fopen(filename, "rb"); 
    if (file == NULL) {  
        printf("Error opening file: %s \n", filename);  
        return ;  
    }
    size_t itemsRead = fread(data, sizeof(char), size, file);  

    if (itemsRead != size) {  
        printf("Error reading file: Not enough data\n");  
        fclose(file);  
        return ;  
    } 
    fclose(file); 
}

int write_ddr128_bin (const char * filename, DDR128* ddr, int size ){
    // int size = sizeof(ddr) / sizeof(ddr[0]);
    printf("Size = %d \n", size);
    // 打开二进制文件以写入数据
    FILE *file = fopen(filename, "wb");
    if (file == NULL) {
        perror("无法打开文件");
        return 1;
    }

    // 将结构体数组写入二进制文件
    size_t written = fwrite(ddr, sizeof(DDR128), size, file);
    if (written != size) {
        perror("写入文件时出错");
        fclose(file);
        return 1;
    }

    // 关闭文件
    fclose(file);
    printf("数据已成功写入 %s 文件。\n", filename);
    return 0;
}

void write_ddr128_file(const char * filename, DDR128* ddr, size_t size){
    FILE *file_out = fopen(filename, "w"); 
    int i, j;
    for (i = 0; i < size; i = i + 1){
        for (j = 3; j >= 0; j--){
            fprintf(file_out, "%08x", ddr[i].ddr32[j]);
        }
        fprintf(file_out, "\n");
    } 
    fclose(file_out); // 关闭文件  
}

void write_ddr256_file(const char * filename, DDR128* ddr, size_t size){
    FILE *file_out = fopen(filename, "w"); 
    int i, j;
   for (i = 0; i < size; i = i + 2){
        for (j = 3; j >= 0; j--){
            fprintf(file_out, "%08x", ddr[i+1].ddr32[j]);
        }
        for (j = 3; j >= 0; j--){
            fprintf(file_out, "%08x", ddr[i+0].ddr32[j]);
        }
        fprintf(file_out, "\n");
   } 
   fclose(file_out); // 关闭文件 
}

void log_results_int(const char * filename, int data[TEST_NUM][16*CIM_ROW], size_t row, size_t col){
   FILE * file_out = fopen(filename, "w"); 
   int i, j;
    for (i = 0; i < row; i++){
        for (j = 0; j < col; j++){
            fprintf(file_out, "%08x\n", data[i][j]);
        }
    } 
   fclose(file_out); // 关闭文件  
}

void log_partresults_int(const char * filename, int data[TEST_NUM][16*CIM_ROW][CIM_COL]){
   FILE * file_out = fopen(filename, "w"); 
   int i, j, k;
    for (i = 0; i < TEST_NUM; i++){
        for (j = 0; j < 16*CIM_ROW; j++){
            for (k = 0; k < CIM_COL; k++){
                fprintf(file_out, "%08x,", data[i][j][k]);
            }
            fprintf(file_out, "\n");
        }
    } 
   fclose(file_out); // 关闭文件  
}

void log_results_char(const char * filename, signed char data[TEST_NUM][16*CIM_ROW], size_t row, size_t col){
   FILE * file_out = fopen(filename, "w"); 
   int i, j;
    for (i = 0; i < row; i++){
        for (j = 0; j < col; j++){
            fprintf(file_out, "%02x\n", (unsigned char)data[i][j]);
        }
    } 
   fclose(file_out); // 关闭文件  
}


void log_char(const char * filename, char *data, size_t size){
   FILE * file_out = fopen(filename, "w"); 
   int i, j;
    for (i = 0; i < size; i++){
        fprintf(file_out, "%02x\n", (unsigned char)data[i]);
    } 
   fclose(file_out); // 关闭文件  
}


void log_256char(const char * filename, char *data, size_t size){
   FILE * file_out = fopen(filename, "w"); 
   int i, j;
    for (i = 0; i < size/32; i++){
        for (j = 31; j >= 0; j--){
            fprintf(file_out, "%02x", (unsigned char)data[i*32+j]);
        }
        fprintf(file_out, "\n");        
    } 
   fclose(file_out); // 关闭文件  
}

void random_data_init(char *data, size_t size){
    int i;
    for (i = 0; i < size; i++){
        data[i] = rand ();
    }
}

int main ()
{
    int num, row, i, j, k;
    char act_data[TEST_NUM][CIM_COL][128];
    char act_read[TEST_NUM*CIM_COL*128];
    char wet_data[16*CIM_ROW][128*CIM_COL] ;
    char wet_read[16*CIM_ROW*128*CIM_COL];
    DDR128 ddr0[TEST_NUM*CIM_COL*8];
    DDR128 ddr1[16*CIM_ROW*CIM_COL*8];

    // char act_data_1[TEST_NUM][CIM_COL][128];
    // DDR128 ddr0_1[TEST_NUM*CIM_COL*8];

    // Read input & weight

    // read_bin_file("./input/wet.bin", wet_read, 16*CIM_ROW*128*CIM_COL);
    // read_bin_file("./input/input.bin", act_read, TEST_NUM*CIM_COL*128);
    random_data_init(wet_read, 16*CIM_ROW*128*CIM_COL);
    random_data_init(act_read, TEST_NUM*CIM_COL*128); 
    


    log_char("./debug/wet.txt", wet_read, 16*CIM_ROW*128*CIM_COL);
    log_char("./debug/act.txt", act_read, TEST_NUM*CIM_COL*128);
    log_256char("./debug/act256.txt", act_read, TEST_NUM*CIM_COL*128);
    log_256char("./debug/wet256.txt", wet_read, 16*CIM_ROW*128*CIM_COL);
    for (i = 0; i < TEST_NUM; i++){
        for (j = 0; j < CIM_COL; j++){
            for (k = 0; k < 128; k++){
                act_data[i][j][k] = act_read[i*CIM_COL*128 + j*128 + k];
                // if(k == 2 )
                //     act_data[i][j][k] = 1;
                // else 
                //     act_data[i][j][k] = 0;
                // act_data[i][j][k] = 1;
                // act_data[i][j][k] = k;
            }
        }
    }
    for (i = 0; i < 16*CIM_ROW; i++){
        for (j = 0; j < 128*CIM_COL; j++){
            wet_data[i][j] = wet_read[i*128*CIM_COL + j];
            // wet_data[i][j] = 36;
            // wet_data[i][j] = i%CIM_ROW;
            // wet_data[i][j] = j%128;
        }
    }
    // wet_data[0][0] = 1;
    // wet_data[1][1] = 1;
    // wet_data[2][2] = 1;


    // prepare ddr0 for act data
    act_datatransfer(TEST_NUM, CIM_COL, act_data, ddr0);
    write_ddr256_file("./output/ddr256_act.txt", ddr0, TEST_NUM*CIM_COL*8);
    write_ddr128_file("./output/ddr128_act.txt", ddr0, TEST_NUM*CIM_COL*8);
    write_ddr128_bin("./output/ddr128_act.bin", ddr0, sizeof(ddr0)/sizeof(ddr0[0]));
    // prepare dd1 for weight data
    wet_datatransfer(CIM_ROW, CIM_COL, wet_data, ddr1);
    write_ddr256_file("./output/ddr256_wet.txt", ddr1, 16*CIM_ROW*CIM_COL*8);
    write_ddr128_file("./output/ddr128_wet.txt", ddr1, 16*CIM_ROW*CIM_COL*8);

    write_ddr128_bin("./output/ddr128_wet.bin", ddr1, sizeof(ddr1)/sizeof(ddr1[0]));


    // software result 
    int golden_out_data_full[TEST_NUM][16*CIM_ROW];
    int golden_out_data_shift[TEST_NUM][16*CIM_ROW];
    int golden_out_data_part[TEST_NUM][16*CIM_ROW][CIM_COL];
    signed char golden_out_data_char[TEST_NUM][16*CIM_ROW];


    for ( num = 0; num < TEST_NUM; num++) {
        for ( row = 0; row < 16*CIM_ROW; row++) {
            for ( i = 0; i < CIM_COL; i++) {
                golden_out_data_part[num][row][i] = 0;
                for ( j = 0; j < 128; j ++) {
                    golden_out_data_part[num][row][i] = golden_out_data_part[num][row][i]  + wet_data[row][128*i+j] * act_data[num][i][j]; 
                }
            }
        }
    }
    for ( num = 0; num < TEST_NUM; num++) {
        for ( row = 0; row < 16*CIM_ROW; row++) {
            golden_out_data_full[num][row] = 0;
            for ( i = 0; i < CIM_COL; i++) {
                for ( j = 0; j < 128; j ++) {
                    golden_out_data_full[num][row] = golden_out_data_full[num][row] + wet_data[row][128*i+j] * act_data[num][i][j]; 
                }
            }
        }
    }
    for ( num = 0; num < TEST_NUM; num++) {
        for ( row = 0; row < 16*CIM_ROW; row++) {
            golden_out_data_shift[num][row] = golden_out_data_full[num][row] >> SHIFT_NUM;// / (1<<SHIFT_NUM);
            if(golden_out_data_shift[num][row] > 127) {
                golden_out_data_char[num][row]  = 127;
            }else if(golden_out_data_shift[num][row] < -128){
                golden_out_data_char[num][row]  = -128;
            }else {
                golden_out_data_char[num][row]  = golden_out_data_shift[num][row] ;
            }
        }
    }
 

    // LOG Resuts
    printf("LOG Result \n");
    log_results_int("./debug/golden_out_data_full.txt", golden_out_data_full, TEST_NUM, 16*CIM_ROW);
    log_results_int("./debug/golden_out_data_shift.txt", golden_out_data_shift, TEST_NUM, 16*CIM_ROW); 
    log_partresults_int("./debug/golden_out_data_part.txt", golden_out_data_part);
    log_results_char("./debug/golden_out_data_char.txt", golden_out_data_char, TEST_NUM, 16*CIM_ROW); 




// //=================== 2 layer ===============================
// for (i = 0; i < TEST_NUM; i++){
//     for (j = 0; j < CIM_COL; j++){
//         if(j==0){
//             for (k = 0; k < 128; k++){
//                 act_data_1[i][j][k] = golden_out_data_char[i][k];
//             }
//         }
//         else if(j==1){
//             for (k = 0; k < 64; k++){
//                 act_data_1[i][j][k] = golden_out_data_char[i][128 + k];
//             }
//             for (k = 64; k < 128; k++){
//                 act_data_1[i][j][k] = 0;
//             }                          
//         }
//         else{
//             for (k = 0; k < 128; k++){
//                 act_data_1[i][j][k] = 0;
//             }
//         }
//     }
// }
//     // prepare ddr0 for act data
//     act_datatransfer(TEST_NUM, CIM_COL, act_data_1, ddr0_1);
//     write_ddr256_file("./output2layer/ddr256_act1.txt", ddr0_1, TEST_NUM*CIM_COL*8);
//     write_ddr128_file("./output2layer/ddr128_act1.txt", ddr0_1, TEST_NUM*CIM_COL*8);

//     // software result 
//     int golden_out_data_full_1[TEST_NUM][16*CIM_ROW];
//     int golden_out_data_shift_1[TEST_NUM][16*CIM_ROW];
//     int golden_out_data_part_1[TEST_NUM][16*CIM_ROW][CIM_COL];
//     signed char golden_out_data_char_1[TEST_NUM][16*CIM_ROW];

//     for ( num = 0; num < TEST_NUM; num++) {
//         for ( row = 0; row < 16*CIM_ROW; row++) {
//             for ( i = 0; i < CIM_COL; i++) {
//                 golden_out_data_part_1[num][row][i] = 0;
//                 for ( j = 0; j < 128; j ++) {
//                     golden_out_data_part_1[num][row][i] = golden_out_data_part_1[num][row][i]  + wet_data[row][128*i+j] * act_data_1[num][i][j]; 
//                 }
//             }
//         }
//     }
//     for ( num = 0; num < TEST_NUM; num++) {
//         for ( row = 0; row < 16*CIM_ROW; row++) {
//             golden_out_data_full_1[num][row] = 0;
//             for ( i = 0; i < CIM_COL; i++) {
//                 for ( j = 0; j < 128; j ++) {
//                     golden_out_data_full_1[num][row] = golden_out_data_full_1[num][row] + wet_data[row][128*i+j] * act_data_1[num][i][j]; 
//                 }
//             }
//         }
//     }
//     for ( num = 0; num < TEST_NUM; num++) {
//         for ( row = 0; row < 16*CIM_ROW; row++) {
//             golden_out_data_shift_1[num][row] = golden_out_data_full_1[num][row] >> SHIFT_NUM;// / (1<<SHIFT_NUM);
//             if(golden_out_data_shift_1[num][row] > 127) {
//                 golden_out_data_char_1[num][row]  = 127;
//             }else if(golden_out_data_shift_1[num][row] < -128){
//                 golden_out_data_char_1[num][row]  = -128;
//             }else {
//                 golden_out_data_char_1[num][row]  = golden_out_data_shift_1[num][row] ;
//             }
//         }
//     }
//         // LOG Resuts

//     log_results_int("./output2layer/golden_out_data_full_1.txt", golden_out_data_full_1, TEST_NUM, 16*CIM_ROW);
//     log_results_int("./output2layer/golden_out_data_shift_1.txt", golden_out_data_shift_1, TEST_NUM, 16*CIM_ROW); 
//     log_partresults_int("./output2layer/golden_out_data_part_1.txt", golden_out_data_part_1);
//     log_results_char("./output2layer/golden_out_data_char_1.txt", golden_out_data_char_1, TEST_NUM, 16*CIM_ROW); 
 
}
